import sys
import math, random
import os

global meanAction
meanAction = 50
global NumberofRounds 
global EXPLOIT, OBSERVE, INNOVATE, REFINE
global p_c, n_observe, p_copyFail, n_migrate, r_max, p_max
global reproduction_probablity
global PayOffs
global extension
r_max = 500

#Function
def poisson_distribution(x):
    cumulative_probability = random.random()
    k = 0
    p = pow(x,k)/(math.factorial(k))*pow(math.e,-x)
    while cumulative_probability > p:
        k = k + 1
        p = p + pow(x,k)/(math.factorial(k))*pow(math.e,-x)
            
    return (k)

def init_payoff(payoff):
    k = 1 
    while k<=NumberofAction:
        payoff[k] = poisson_distribution(meanAction)
        k = k+1
    return payoff

def INNOVATE_function(payoff):
    k = random.randint(1,NumberofAction)
    return (k, payoff[k])
    
def OBSERVE_function(CurrentRoundsExploit, CurrentRoundsExploit_payoff, CurrentRoundsExploitCount):
    SelectExploit = [0] * CurrentRoundsExploitCount
    ObserveData = [0, 0] * n_observe;
    Sample = random.sample(CurrentRoundsExploit, n_observe)
    
    for i in range(0, n_observe):
        k = Sample[i]
        
        if (i >= CurrentRoundsExploitCount):
            ObserveData[i][0] = CurrentRoundsExploit[k]
        
        ObserveData[i][0] = CurrentRoundsExploit[k]
        
        estimatedpayoff = poisson_distribution(CurrentRoundsExploit_payoff[k])
        
        ObserveData[i][1] = estimatedpayoff
        
    return ObserveData
    
def REFINE_cal_increment(r):
    global meanAction, r_max
    p_max = meanAction * 50
    
    increment = 0.0

    r = int(r)
    
    #sum = range[1, r+1]
    sum = [0] * (r+1)
    
    k=0
    while(k<len(sum)):
        sum[k] = pow(0.95, r-(k+1))
        k = k + 1
    
    increment = 0.05 / (1-pow(0.95, r_max)) * math.fsum(sum) * p_max
    
    return increment

def REFINE_function(Ind_Number, ActionNumber):
    global Individuals;
    Individuals[Ind_Number].RefineLevel[ActionNumber] = Individuals[Ind_Number].RefineLevel[ActionNumber]+1 
    relevel2 = int(Individuals[Ind_Number].RefineLevel[ActionNumber])
    REFINE_value = REFINE_cal_increment(relevel2)
    return Payoffs[ActionNumber] + REFINE_value

def EXPLOIT_function(ActionNumber, RE):
    global Payoffs
    relevel2 = int(RE[ActionNumber])
    REFINE_value = REFINE_cal_increment(relevel2)
    ans = Payoffs[ActionNumber] + REFINE_value
    return ans

#variable


INNOVATE = -1
OBSERVE = 0
EXPLOIT = 1
REFINE = 2

NumberofRounds = 10000
n_observe = 1
p_c = 0.02

NumberofStrategy = 2
NumberofPopulation = 100
NumberofAction = 100

#reproduction_probablity =  

Payoffs = [0] * (NumberofAction + 1)
Payoffs = init_payoff(Payoffs)
    
CurrentRoundsMoves = [0, 0] * ((NumberofPopulation + 1) * NumberofStrategy)
CurrentRoundsFlag = [0] * ((NumberofPopulation + 1) * NumberofStrategy)

NumberofRounds = 10000

Sumofpopulation = NumberofStrategy * NumberofPopulation

CurrentRoundsExploit = list()
CurrentRoundsExploit_payoff = list()
CurrentRoundsExploitCount = 0

#

class individual:
    def __init__ (self):
        self.alive = 1
        self.lifetime = 1
    
        self.meanlifepayoff = 0
    
        self.historyMoves = [0] * (NumberofRounds + 2)
        self.historyRounds = [0] * (NumberofRounds + 2)
        self.historyActs = [0] * (NumberofRounds + 2)
        self.historyPayOffs = [0] * (NumberofRounds + 2)
        self.historyDemes = [0] * (NumberofRounds + 2)
        self.myRepertoire = [0] * (NumberofAction + 10)    
    
        self.RefineLevel = [0] * (NumberofAction + 2)
    
        self.CurrentDemes = 0
        self.Count = 0 #This mean history's last element's index
        self.Strategy = 0
    
    def move(self, currentrounds):
        if (self.Count == 0):
            return (INNOVATE, 0)
        if (self.Count % 2 == 1):
            return (EXPLOIT, self.historyActs[self.Count-1])
        return (INNOVATE, 0)
    
    def meanlifetimepayoff_cal(self):
        sumofpayoff = 0
        for i in range(self.Count): 
            if (self.historyMoves[i] == EXPLOIT):
                sumofpayoff = sumofpayoff + self.myRepertoire[self.historyActs[i]]
        return sumofpayoff / self.lifetime
#

global Individuals

Individuals = list()
StrategyList = [0] * NumberofStrategy

for j in range(0, NumberofStrategy):
    for i in range(j * NumberofPopulation, (j+1) * NumberofPopulation):
        Individuals.append(individual())
        Individuals[i].Strategy = StrategyList[j] #this individual's Strategy is j
        Individuals[i].lifetime = 0


        
for j in range(Sumofpopulation):
    file2 = open(os.path.dirname(os.path.abspath(__file__)) + "/individual/individual" + str(j) + ".txt", 'w')
    file2.close()
print("Simulator Start.")

PrintMode = 1;

if (PrintMode == 0):
    file1 = open(os.path.dirname(os.path.abspath(__file__)) + "/rounds_play.txt", 'w')
    
Sumofmeanlifetimepayoff = 0 
NumberofRounds = 10;

BornNumber = 0;
Bornthisround = 0

for i in range(1, NumberofRounds + 1): 
    if (PrintMode == 0):
        file1.write(str(i) + " nd Rounds Start")
        file1.write('\n')
    if (random.random() < p_c):
        Payoffs = init_payoff(Payoffs)
    Diethisround = 0
    Bornthisround = 0    
    #initialize CurrentRoundsExploit data
    
    CurrentRoundsFlag = list()
    CurrentRoundsMoves= list()
    
    CurrentRoundsFlag.append([0])
    CurrentRoundsMoves.append([0, 0])
    
    for j in range(Sumofpopulation):
        CurrentRoundsFlag.append([0])
        CurrentRoundsMoves.append([0, 0])
        
    CurrentRoundsExploitCount = 0
    CurrentRoundsExploit = list()
    CurrentRoundsExploit_payoff = list()
    
    for j in range(Sumofpopulation):
        if (Individuals[j].alive == 1):
            if (PrintMode == 0):
                file1.write("Individual " + str(j) + " is Still Alive")
                file1.write('\n') 
            if (random.random() < 0.02):
                #file1.write(" But Die...")
                Individuals[j].alive = 0                
                Diethisround = Diethisround + 1

            
             
    for j in range(Sumofpopulation):
        if (Individuals[j].alive == 1):
            #CurrentRoundsMoves[j] = StrategyN_move(j, Individuals[j].Strategy, i, Individuals[j].myRepertoire, Individuals[j].historyRounds, Individuals[j].historyActs, Individuals[j].historyPayOffs, Individuals[j].historyDemes, Individuals[j].CurrentDemes, 0, 0, 0, 0);
            Individuals[j].lifetime = Individuals[j].lifetime + 1
            CurrentRoundsMoves[j] = Individuals[j].move(i)

            m = Individuals[j].meanlifetimepayoff_cal()
            Individuals[j].meanlifepayoff = m  
            
            #print("Individual ", j , " is ", CurrentRoundsMoves[j][0], "in this round");
            if (PrintMode == 0):
                file1.write("Individual " + str(j) + " is " + str(CurrentRoundsMoves[j][0]) + " in this round")
                file1.write('\n')
        if (Individuals[j].alive == 0):
            if (PrintMode == 0):
                file1.write("Individual " + str(j) + " is DIE")
                file1.write('\n')
            CurrentRoundsMoves[j][0]=0
            CurrentRoundsMoves[j][1]=0
    
    #Innovate
    for j in range(Sumofpopulation):
        if (Individuals[j].alive == 0):
            continue
        if (CurrentRoundsMoves[j][0] == INNOVATE):
            if (PrintMode == 0):
                file1.write("Individual " + str(j) + " INNOVATE")
                file1.write('\n')
            #
            
            INNOVATE_ACT = INNOVATE_function(Payoffs)
            
            Individuals[j].historyRounds[Individuals[j].Count] = i
            Individuals[j].historyMoves[Individuals[j].Count] = INNOVATE
            Individuals[j].historyPayOffs[Individuals[j].Count] = INNOVATE_ACT[1]
            Individuals[j].historyActs[Individuals[j].Count] = INNOVATE_ACT[0]
        
            t1 = INNOVATE_ACT[0]
            t2 = INNOVATE_ACT[1]
            
            Individuals[j].myRepertoire[t1] = t2
            
            #write the current round's log
            
            Individuals[j].Count = Individuals[j].Count + 1    
            
        #print("E")
        
    for j in range(Sumofpopulation):
        if (Individuals[j].alive == 0):
            continue
        if (int(CurrentRoundsMoves[j][0]) == EXPLOIT):
            if (PrintMode == 0):
                file1.write("Individual " + str(j) + " EXPLOIT")
                file1.write('\n')
            #
            
            Individuals[j].historyRounds[int(Individuals[j].Count)] = i
            Individuals[j].historyMoves[int(Individuals[j].Count)] = EXPLOIT
            
            Estivalue = EXPLOIT_function(int(CurrentRoundsMoves[j][1]), Individuals[j].RefineLevel)
            
            Individuals[j].historyPayOffs[Individuals[j].Count] = Payoffs[int(CurrentRoundsMoves[j][1])]
            Individuals[j].historyActs[Individuals[j].Count] = CurrentRoundsMoves[j][1]
            
            Individuals[j].Count = Individuals[j].Count + 1             
            
            CurrentRoundsExploit.append(CurrentRoundsMoves[j][1])
            CurrentRoundsExploit_payoff.append(Estivalue)
    
    for j in range(Sumofpopulation):
        if (Individuals[j].alive == 0):
            continue
        if (CurrentRoundsMoves[j][0] == OBSERVE):
            if (PrintMode == 0):
                file1.write("Individual " + str(j) + " OBSERVE")
                file1.write('\n')
            #
            Individuals[j].historyMoves.append([0] * (n_observe - 1))                                               
            Individuals[j].historyRounds.append([0] * (n_observe - 1))
            Individuals[j].historyActs.append([0] * (n_observe - 1))
            Individuals[j].historyPayOffs.append([0] * (n_observe - 1))
            Individuals[j].historyDemes.append([0] * (n_observe - 1))                                                                               
            #
            
            observedpayoff = OBSERVE_function(CurrentRoundsExploit, CurrentRoundsExploit_payoff, CurrentRoundsExploitCount)
            
            for k in range(1, n_observe):
            #
                Individuals[j].historyRounds[Individuals[j].Count] = i
                Individuals[j].historyMoves[Individuals[j].Count] = OBSERVE
                Individuals[j].historyPayOffs[Individuals[j].Count] = observedpayoff[k-1][1]
                Individuals[j].historyActs[Individuals[j].Count] = observedpayoff[k-1][0]
                Individuals[j].historyDemes[Individuals[j].Count] = 0
                Individuals[j].myRepertoire[observedpayoff[k-1][0]] = observedpayoff[k-1][1]
                
                Individuals[j].Count = Individuals[j].Count + 1
            #
            
    for j in range(Sumofpopulation):
        if (Individuals[j].alive == 0):
            continue
        if (CurrentRoundsMoves[j][0] == REFINE):
            if (PrintMode == 0):
                file1.write("Individual " + str(j) + " REFINE")
                file1.write('\n')
            #
            Individuals[j].historyMoves.append([0] * (n_observe - 1))                                               
            Individuals[j].historyRounds.append([0] * (n_observe - 1))
            Individuals[j].historyActs.append([0] * (n_observe - 1))
            Individuals[j].historyPayOffs.append([0] * (n_observe - 1))
            Individuals[j].historyDemes.append([0] * (n_observe - 1))                                                                               
            
            IndividualroundsREFINE = REFINE_function(Individuals[j].RefineLevel, CurrentRoundsMoves[j][1])
            
            for k in range(1, n_observe):
                Individuals[j].historyRounds[Individuals[j].Count] = i
                Individuals[j].historyMoves[Individuals[j].Count] = REFINE
                Individuals[j].historyPayOffs[Individuals[j].Count] = Payoffs[IndividualroundsREFINE[k-1]]
                Individuals[j].historyActs[Individuals[j].Count] = IndividualroundsREFINE[k-1]
                Individuals[j].historyDemes[Individuals[j].Count] = 0
                Individuals[j].Count = Individuals[j].Count + 1
            #
    if (PrintMode == 0):
        for j in range(Sumofpopulation):
            if (PrintMode == 0):
                file2 = open(os.path.dirname(os.path.abspath(__file__)) + "/individual/individual" + str(j) + ".txt", 'a+')
                file2.write(str(i) + ' nd rounds : Individuals['+ str(j) + '].Count = ' + str(Individuals[j].Count) + '\n' + str(Individuals[j].meanlifepayoff) + '\n')
                for k in range(0, Individuals[j].Count):
                    file2.write(str(int(Individuals[j].historyRounds[k])) + ' ')
                file2.write('\n')
                for k in range(0, Individuals[j].Count):
                    file2.write(str(Individuals[j].historyMoves[k]) + ' ')
                file2.write('\n')
                for k in range(0, Individuals[j].Count):
                    file2.write(str(Individuals[j].historyActs[k]) + ' ')
                file2.write('\n')
                for k in range(0, Individuals[j].Count):
                    file2.write(str(Individuals[j].historyPayOffs[k]) + ' ')
                file2.write('\n')
                for k in range(0, Individuals[j].Count):
                    file2.write(str(Individuals[j].historyDemes[k]) + ' ')
                file2.write('\n')
                #file2.write(str(j))
                file2.write('-------\n')
                #file2.close()
        
    Sumofmeanlifetimepayoff = 0
    
    for j in range(0, Sumofpopulation):
        if (Individuals[j].alive == 1):
            Sumofmeanlifetimepayoff = Sumofmeanlifetimepayoff + Individuals[j].meanlifepayoff
    
    S = Sumofpopulation    
    
    for j in range(S):
        if(Sumofmeanlifetimepayoff == 0):
            continue
                    
        if (Individuals[j].alive == 1):
            m = Individuals[j].meanlifetimepayoff_cal()
            Individuals[j].meanlifepayoff = m
              
            reproduction_rate = Individuals[j].meanlifepayoff / Sumofmeanlifetimepayoff
            
            if (random.random() < reproduction_rate):
                Individuals.append(individual())
                Sumofpopulation = Sumofpopulation + 1            
                Individuals[-1].Strategy = Individuals[j].Strategy
                
                Bornthisround = Bornthisround + 1
                BornNumber = BornNumber + 1
                
                if random.random() < 0.02:
                    #print("mutation begins");
                    if (PrintMode == 0):
                        file1.write("Individual " + str(j) + " occur mutation")
                        file1.write('\n')
                    Individuals.append(individual())
                    Sumofpopulation = Sumofpopulation + 1            
                    Individuals[-1].Strategy = random.randint(0, NumberofStrategy-1)
                    Bornthisround = Bornthisround + 1
                    BornNumber = BornNumber + 1
            #
                
            #
    if (PrintMode == 0):            
        file1.write(str(i) + ' nd Rounds Ends')
        file1.write('\n')
    
    #etc...
    if (PrintMode == 0):

        print(i, "nd Round")
        print("Die ", Diethisround)
        print("Born ", Bornthisround)    
        alivepop = 0

        for uu in range(Sumofpopulation):
            if (Individuals[uu].alive == 1):
                alivepop = alivepop + 1
        print("alive : ", alivepop)
    if (PrintMode == 1):
        #if ((i % 500) == 0):
        #for j in range(0, NumberofAction):
            
        print("Round ", i, "nd")

if(PrintMode == 0):        
    file1.write("Simulator Ends.")
    file1.close()

print("Simulator Ends.")
print(Sumofmeanlifetimepayoff)
alivepop = 0
diepop = 0

for i in range(Sumofpopulation):
    if (Individuals[i].alive == 1):
        alivepop = alivepop + 1

for i in range(Sumofpopulation):
    if (Individuals[i].alive == 0):
        diepop = diepop + 1
         
print(alivepop)
print(BornNumber)
    
 
